//****************************************************
// Porman Project 2
// Filename: CDeviceCreator.cpp
// Date: 2007/10/07
// Author: KengYu Lin
// Modify:
//****************************************************
#include "CDeviceCreator.h"
#include "CRenderDevice.h"

namespace Porman
{

namespace GL
{

//--------------------------------
// class CDeviceDesc
//--------------------------------
CDeviceDesc::CDeviceDesc( void ) :
m_vectOptionInt(), 
m_vectOptionFloat(), 
m_pAttributesInt(NULL), 
m_pAttributesFloat(NULL)
{
    setFormat( E_DRAW_TO_WINDOW, 1 );
    setFormat( E_SUPPORT_OPENGL, 1 );
    setFormat( E_ACCELERATION, E_FULL_ACCELERATION );
    setFormat( E_COLOR_BITS, 24 );
    setFormat( E_ALPHA_BITS, 8 );
    setFormat( E_DEPTH_BITS, 16 );
    setFormat( E_STENCIL_BITS, 0 );
    setFormat( E_DOUBLE_BUFFER, 1 );
    setFormat( E_SWAP_METHOD, E_SWAP_EXCHANGE );
}

CDeviceDesc::CDeviceDesc( const CDeviceDesc& l ) :
m_pAttributesInt(NULL), 
m_pAttributesFloat(NULL)
{ 
    (*this) = l; 
}

CDeviceDesc::~CDeviceDesc( void )
{
    m_vectOptionInt.clear();
    m_vectOptionFloat.clear();

    if(m_pAttributesInt)
    {
        SAFEDELETE_ARRAY(m_pAttributesInt);
    }

    if(m_pAttributesFloat)
    {
        SAFEDELETE_ARRAY(m_pAttributesFloat);
    }
}

CDeviceDesc& CDeviceDesc::operator=( const CDeviceDesc& l )
{
    m_vectOptionInt = l.m_vectOptionInt;
    m_vectOptionFloat = l.m_vectOptionFloat;

    if(m_pAttributesInt)
    {
        SAFEDELETE_ARRAY(m_pAttributesInt);
    }

    if(m_pAttributesFloat)
    {
        SAFEDELETE_ARRAY(m_pAttributesFloat);
    }

    return (*this);
}

void CDeviceDesc::setColorFormat( int uiRedBits, int uiGreenBits, int uiBlueBits, int uiAlphaBits )
{
    int iBits = uiRedBits + uiGreenBits + uiBlueBits + uiAlphaBits;
    setFormat( E_COLOR_BITS, iBits );
    setFormat( E_RED_BITS, uiRedBits );
    setFormat( E_GREEN_BITS, uiGreenBits );
    setFormat( E_BLUE_BITS, uiBlueBits );
    setFormat( E_ALPHA_BITS, uiAlphaBits );
}

void CDeviceDesc::setDepthFormat( int uiBits )
{
    setFormat( E_DEPTH_BITS, uiBits );
}

void CDeviceDesc::setStencilFormat( int uiBits )
{
    setFormat( E_STENCIL_BITS, uiBits );
}

void CDeviceDesc::setDoubleBuffer( bool bEnable )
{
    int i = bEnable ? 1:0;
    setFormat( E_DOUBLE_BUFFER, i );
}

void CDeviceDesc::setSwapMethod( EDrawSurfaceSwapMethod enumSwapMethod )
{
    setFormat( E_SWAP_METHOD, enumSwapMethod );
}

void CDeviceDesc::setAcceleration( EDrawSurfaceAcceleration enumAcceleration )
{
    setFormat( E_ACCELERATION, enumAcceleration );
}

void CDeviceDesc::setFormat( int iOption, int iValue )
{
    int iSize = (int)m_vectOptionInt.size();
    for(int i=0; i<iSize; i++)
    {
        if( m_vectOptionInt[i].m_iOption == iOption )
        {
            m_vectOptionInt[i].m_iValue = iValue;
            return;
        }
    }

    SOptionInt IntOp;
    IntOp.m_iOption = iOption;
    IntOp.m_iValue  = iValue;

    m_vectOptionInt.push_back( IntOp );
    return;
}

void CDeviceDesc::setFormat( float fOption, float fValue )
{
    int iSize = (int)m_vectOptionFloat.size();
    for(int i=0; i<iSize; i++)
    {
        if( m_vectOptionFloat[i].m_fOption == fOption )
        {
            m_vectOptionFloat[i].m_fValue = fValue;
            return;
        }
    }

    SOptionFloat FloatOp;
    FloatOp.m_fOption = fOption;
    FloatOp.m_fValue  = fValue;

    m_vectOptionFloat.push_back( FloatOp );
}

int* CDeviceDesc::_outputAttributesInt( void ) const
{
    if(m_pAttributesInt)
    {
        SAFEDELETE_ARRAY(m_pAttributesInt);
    }

    SOptionInt OpInt;
    OpInt.m_iOption = 0;
    OpInt.m_iValue  = 0;

    m_vectOptionInt.push_back( OpInt );

    int iSize = (int)m_vectOptionInt.size();
    if(iSize > 0)
    {
        m_pAttributesInt = new int[iSize*2];
        
        int* pArray = m_pAttributesInt;
        for(int i = 0; i<iSize; i++)
        {
            memcpy( pArray, m_vectOptionInt[i].m_iV, sizeof(int)*2 );
            pArray += 2;
        }
    }

    return m_pAttributesInt; 
}

float* CDeviceDesc::_outputAttributesFloat( void ) const
{ 
    if(m_pAttributesFloat)
    {
        SAFEDELETE_ARRAY(m_pAttributesFloat);
    }

    SOptionFloat OpFloat;
    OpFloat.m_fOption = 0;
    OpFloat.m_fValue  = 0;

    m_vectOptionFloat.push_back( OpFloat );

    int iSize = static_cast<int>( m_vectOptionFloat.size() );
    if(iSize > 0)
    {
        m_pAttributesFloat = new float[iSize*2];

        float* pArray = m_pAttributesFloat;
        for(int i = 0; i<iSize; i++)
        {
            memcpy( pArray, m_vectOptionFloat[i].m_fV, sizeof(float)*2 );
            pArray += 2;
        }
    }

    return m_pAttributesFloat; 
}

//--------------------------------
// class CDeviceCreator
//--------------------------------
ERESULT 
CDeviceCreator::createCanvas( const HWND hWnd, const CDeviceDesc* pSurfaceDec, CRenderCanvas** ppCanvas )
{
    ERESULT enumResult = E_SUCCESS;
    if( hWnd && pSurfaceDec )
    {
        int iPixelFormat;
        enumResult = _choosePixelFormat( hWnd, pSurfaceDec, &iPixelFormat );
        if( enumResult == E_SUCCESS )
        {
            HDC hDC = GetDC( hWnd );
            if( hDC )
            {
                PIXELFORMATDESCRIPTOR pfd;
                ZeroMemory( &pfd, sizeof(pfd) );

                if( SetPixelFormat( hDC, iPixelFormat, &pfd ) )
                {
                    CRenderCanvas* pCanvas = new CRenderCanvas();
                    pCanvas->m_hWnd = hWnd;
                    pCanvas->m_hDC  = hDC;
                    pCanvas->m_iPixelFormat = iPixelFormat;

                    (*ppCanvas) = pCanvas;
                    return enumResult;
                }
            }
        }
    }
    else
        enumResult = E_INVALID_VALUE;

    ppCanvas = NULL;
    assert(0);
    return enumResult;
}

void
CDeviceCreator::destroyCanvas( CRenderCanvas** ppCanvas )
{
    SAFEDELETE( (*ppCanvas) );
}

ERESULT 
CDeviceCreator::createDevice( const CRenderCanvas* pSurface, CRenderDevice** ppRenderDevice )
{
    ERESULT enumResult = E_SUCCESS;
    if( pSurface )
    {
        if( GLEE_VERSION_2_0 )
        {
            CRenderDevice* Device = new CRenderDevice();
            enumResult = Device->_initial( pSurface );
            if( enumResult == E_SUCCESS )
            {
                (*ppRenderDevice) = Device;
                return enumResult;
            }
            else
            {
                SAFEDELETE( Device );
            }
        }
        else
            enumResult = E_OGL_VERSION_NOT_SUPPORT;
    }
    else
        enumResult = E_INVALID_VALUE;

    assert(0);
    return enumResult;
}

void 
CDeviceCreator::destroyDevice( CRenderDevice** ppRenderDevice )
{
    SAFEDELETE( (*ppRenderDevice) );
}

ERESULT 
CDeviceCreator::_choosePixelFormat( const HWND hWnd, const CDeviceDesc* pSurfaceDec, int* pOutPixelFormat )
{
    ERESULT enumResult = E_SUCCESS;
    HGLRC hRC_Dummy  = NULL;
    HWND  hWnd_Dummy = NULL;
    HDC   hDC_Dummy  = NULL;

    HDC hDC = GetDC( hWnd );
    if( hDC )
    {
        PIXELFORMATDESCRIPTOR pfd;
        ZeroMemory( &pfd, sizeof(pfd) );

        if( !wglGetCurrentContext() )
        {
            CHAR ClassName[100];
            GetClassName(hWnd,ClassName,100);
            hWnd_Dummy = CreateWindow(ClassName,
                                      "",
                                      WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                                      0,0,1,1,
                                      hWnd,
                                      0,0,0);

            if( hWnd_Dummy )
            {
                hDC_Dummy = GetDC( hWnd_Dummy );
                if( hDC_Dummy && SetPixelFormat( hDC_Dummy, 1, &pfd ) )
                {
                    hRC_Dummy = wglCreateContext( hDC_Dummy );
                    if( hRC_Dummy )
                    {
                        if( !wglMakeCurrent( hDC_Dummy, hRC_Dummy ) )
                        {
                            wglDeleteContext( hRC_Dummy );
                            enumResult = E_INVALID_OPERATION;
                            goto error;
                        }
                    }
                    else
                    {
                        enumResult = E_INVALID_OPERATION;
                        goto error;
                    }
                }
                else
                {
                    enumResult = E_INVALID_OPERATION;
                    goto error;
                }
            }
            else
            {
                enumResult = E_INVALID_OPERATION;
                goto error;
            }
        }//End if( !wglGetCurrentContext() )

        unsigned int uiNumFormats;
        bool bResult = wglChoosePixelFormatARB( hDC, 
                                                pSurfaceDec->_outputAttributesInt(), 
                                                pSurfaceDec->_outputAttributesFloat(), 
                                                1, 
                                                pOutPixelFormat, &uiNumFormats ) ? true : false;

        if( hWnd_Dummy )
        {
            if( hRC_Dummy ) wglDeleteContext( hRC_Dummy );
            if( hDC_Dummy ) ReleaseDC( hWnd_Dummy, hDC_Dummy );
            DestroyWindow( hWnd_Dummy );

            hWnd_Dummy = NULL;
            hRC_Dummy  = NULL;
            hDC_Dummy  = NULL;
        }

        if( bResult && uiNumFormats >= 1 )
        {
            int iCurPFormat = GetPixelFormat( hDC );
            if( iCurPFormat != 0 )
            {
                if( iCurPFormat != (*pOutPixelFormat) )
                {
                    (*pOutPixelFormat) = iCurPFormat;
                    return E_WINDOW_ALREADY_EXIST_PIXELFORMAT;
                }
            }
            return enumResult;
        }
        else
            enumResult = E_CREATE_PIXELFORMAT_FAIL;
    }
    else
        enumResult = E_GET_DC_FAIL;

error:
    if( hWnd_Dummy )
    {
        if( hRC_Dummy ) wglDeleteContext( hRC_Dummy );
        if( hDC_Dummy ) ReleaseDC( hWnd_Dummy, hDC_Dummy );
        DestroyWindow( hWnd_Dummy );

        hWnd_Dummy = NULL;
        hRC_Dummy  = NULL;
        hDC_Dummy  = NULL;
    }

    wglMakeCurrent( NULL, NULL );

    assert(0);
    return enumResult;
}

}//End namespace GL

}//End namespace Porman